/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Analyser;

import samples.ElaboratedSampleCollection;
import Analyser.events.*;
import Analyser.events.EventStartAndEnd;
import jWASA.PHR;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import samples.*;

/**
 *
 * @author gianmariospacagna
 */
public class Analyser {

    final static float heartbeatThUp = 140;
    final static float heartbeatThDown = 100;
    final static float breathThUp = 50;
    final static float breathThDown = 30;
    final static int agitateWindow = 10000;
    final static float lightThDown = 10;
    final static float lightThUp = 20;
    final static long darkWindow = 3600000;
    ElaboratedSampleCollection samples;
    Collection<PHR> PHRrecords;

    public void analyseData(ElaboratedSampleCollection samples, Collection<PHR> PHRrecords) {
        Collection<EventAgitation> agitationEvents = findAgitationEvents(samples);
        Collection<EventDark> darkEvents = findDarkEvents(samples);
        Collection<EventScoobyDoo> scoobyDooEvents = findScoobyDooEvents(PHRrecords);
        int totDays = totDaysPHR(PHRrecords);

        int totScoobyDooDays = totDaysEventSingleDay(new ArrayList<EventSingleDay>(scoobyDooEvents));

        long totTimeAgitation = totTimeEventStartAndEnd(new ArrayList<EventStartAndEnd>(agitationEvents));

        //totHeartBeatMeasurementTime ~= totSleepingTime



        ArrayList<ElaboratedSample> heartbeatSamples = new ArrayList<ElaboratedSample>(samples.getHeartbeat());
        Collections.sort(heartbeatSamples);

        Collection<EventStartAndEnd> sleepingEvents = findStartAndEndEvents(heartbeatSamples);

        long totSleepingTime = totElaboratedSampleMeasurementTime(heartbeatSamples);

        long totTimeDark = totTimeEventStartAndEnd(new ArrayList<EventStartAndEnd>(darkEvents));
        long totSleepingTimeDark = overlappingTime(new ArrayList<EventStartAndEnd>(sleepingEvents), new ArrayList<EventStartAndEnd>(darkEvents));

        long overlappingTimeAgitationDark = overlappingTime(new ArrayList<EventStartAndEnd>(agitationEvents), new ArrayList<EventStartAndEnd>(darkEvents));

        int overlappingDaysAgitationScoobyDoo = overlappingDays(new ArrayList<EventStartAndEnd>(agitationEvents), new ArrayList<EventSingleDay>(scoobyDooEvents));

        Collection<EventAgitation> agitationScoobyDooEvents = overlappingEventsAgitationScoobyDoo(agitationEvents, scoobyDooEvents);

        long phantomPhobiaTime = overlappingTime(new ArrayList<EventStartAndEnd>(agitationScoobyDooEvents), new ArrayList<EventStartAndEnd>(darkEvents));

        System.out.println("totDays: " + totDays);
        System.out.println("totScoobyDoodays: " + totScoobyDooDays);
        System.out.println("totTimeAgitation: " + totTimeAgitation / 60000 + " min");
        System.out.println("totSleepingTime: " + totSleepingTime / 60000 + " min");
        System.out.println("totSleepingDays: " + sleepingEvents.size());
        System.out.println("totTimeDark: " + totTimeDark / 60000 + " min");
        System.out.println("totSleepingTimeDark: " + totSleepingTimeDark / 60000 + "min");

        System.out.println("overlappingTimeAgitationDark: " + overlappingTimeAgitationDark / 60000 + " min");
        System.out.println("overlappingDaysAgitationScoobyDoo: " + overlappingDaysAgitationScoobyDoo);
        System.out.println("phantomPhobiaTime: " + phantomPhobiaTime / 60000 + " min");



        //single probabilities

        float probScoobyDoo = (float) totScoobyDooDays / (float) totDays;
        float probDark = (float) totSleepingTimeDark / (float) totSleepingTime;
        float probAgitation = (float) totTimeAgitation / (float) totSleepingTime;

        long totSleepingTimeDarkScoobyDoo = (long) (totSleepingTimeDark * probScoobyDoo);

        //conditionional probabilities
        float probAgitationGivenScoobyDoo = (float) overlappingDaysAgitationScoobyDoo / (float) totScoobyDooDays;
        float probAgitationGivenDark = (float) totTimeAgitation / (float) totSleepingTimeDark;
        float probAgitationGivenScoobyDooAndDark = phantomPhobiaTime / totSleepingTimeDarkScoobyDoo;


        //joint probabilities
        float probAgitationAndScoobyDoo = (float) overlappingDaysAgitationScoobyDoo / (float) totDays;
        float probAgitationAndDark = (float) overlappingTimeAgitationDark / (float) totSleepingTime;
        float probPhantomPhobia = (float) phantomPhobiaTime / (float) totSleepingTime;
        
        //correlations
        float corrPhantomPhobia = probPhantomPhobia / (probScoobyDoo * probAgitation * probDark);
        float corrAgitationAndScoobyDoo = probAgitationAndScoobyDoo / (probAgitation * probScoobyDoo);
        float corrAgitationAndDark = probAgitationAndDark / (probAgitation * probDark);


        System.out.println("Probability ({ScoobyDoo}): " + probScoobyDoo);
        System.out.println("Probability ({Dark}): " + probDark);
        System.out.println("Probability ({Agitation}): " + probAgitation);
        System.out.println("Conditional Probability ({Agitation}/{ScoobyDoo}): " + probAgitationGivenScoobyDoo);
        System.out.println("Conditional Probability ({Agitation}/{Dark}): " + probAgitationGivenDark);
        System.out.println("Conditional Probability ({Agitation}/({ScoobyDoo},{Dark})): " + probAgitationGivenScoobyDooAndDark);
        System.out.println("Joint Probability ({Agitation},{ScoobyDoo}): "+probAgitationAndScoobyDoo);
        System.out.println("Joint Probability ({Agitation},{Dark})"+probAgitationAndDark);
        System.out.println("Joint Probability {PhantomPhobia}: " + probPhantomPhobia);
        System.out.println("Correlation ({Agitation},{ScoobyDoo}): "+corrAgitationAndScoobyDoo);
        System.out.println("Correlation ({Agitation},{Dark}): "+corrAgitationAndDark);
        System.out.println("Correlation {PhantombPhobia}: " + corrPhantomPhobia);
    }

    boolean isAgitate(SampleHeartBeat heartbeat, SampleBreath breath) {

        if (heartbeat.getRate() > heartbeatThUp & breath.getRate() > breathThUp) {
            return true;
        } else {
            return false;
        }
    }

    boolean isNotAgitate(SampleHeartBeat heartbeat, SampleBreath breath) {

        if (heartbeat.getRate() < heartbeatThDown & breath.getRate() < breathThDown) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Find Event Agitation based only on heartbeat rate
     * @param samples
     * @return 
     */
    private Collection<EventAgitation> findAgitationEvents(ElaboratedSampleCollection samples) {

        Collection<EventAgitation> agitationEvents = new ArrayList<EventAgitation>();
        long startTime = 0;
        long endTime = 0;
        boolean agitate = false;

        for (SampleHeartBeat hb : samples.getHeartbeat()) {

            if (!agitate & hb.getRate() > heartbeatThUp) {
                startTime = hb.getTimestamp();
                agitate = true;

            } else if (agitate & hb.getRate() < heartbeatThDown) {
                endTime = hb.getTimestamp();
                if (endTime - startTime >= agitateWindow) {
                    agitationEvents.add(new EventAgitation(startTime, endTime));
                }

                agitate = false;
            }
        }
        return agitationEvents;
    }

    private Collection<EventDark> findDarkEvents(ElaboratedSampleCollection samples) {
        Collection<EventDark> darkEvents = new ArrayList<EventDark>();
        long startTime = 0;
        long endTime = 0;
        boolean dark = false;

        for (SampleLight hb : samples.getLight()) {

            if (!dark & hb.getIntensity() < lightThDown) {

                startTime = hb.getTimestamp();
                dark = true;
                ;
            } else if (dark & hb.getIntensity() > lightThDown) {
                endTime = hb.getTimestamp();
                if (endTime - startTime >= darkWindow) {
                    darkEvents.add(new EventDark(startTime, endTime));
                }

                dark = false;
            }
        }
        return darkEvents;
    }

    private Collection<EventScoobyDoo> findScoobyDooEvents(Collection<PHR> PHRrecords) {

        Collection<EventScoobyDoo> scoobyDooEvents = new ArrayList<EventScoobyDoo>();

        for (PHR phr : PHRrecords) {
            if (phr.getRoutineDescription().contains("ScoobyDoo")) {
                scoobyDooEvents.add(new EventScoobyDoo(phr.getDate()));
            }

        }
        return scoobyDooEvents;
    }

    private int totDaysPHR(Collection<PHR> collection) {
        Set<Date> days = new HashSet<Date>();

        for (PHR phr : collection) {
            days.add(phr.getDate());
        }

        return days.size();

    }

    private int totDaysEventSingleDay(Collection<EventSingleDay> events) {
        Set<Date> days = new HashSet<Date>();

        for (EventSingleDay event : events) {
            days.add(event.getDate());

        }

        return days.size();

    }

    private long totElaboratedSampleMeasurementTime(ArrayList<ElaboratedSample> samples) {

        long prevTime = samples.get(0).getTimestamp();
        long totTime = 0;
        Date day = new Date(prevTime);
        long delta = 0;
        for (ElaboratedSample s : samples) {
            //System.out.println("Date: "+day+" Tot Time: "+totTime);

            if (EventAgitation.sameDay(new Date(s.getTimestamp()), day)) {
                delta = s.getTimestamp() - prevTime;

            } else {
                day = new Date(s.getTimestamp());
            }
            totTime += delta;
            prevTime = s.getTimestamp();
        }


        return totTime;
    }

    private long totTimeEventStartAndEnd(Collection<EventStartAndEnd> events) {
        long totTime = 0;
        for (EventStartAndEnd event : events) {
            totTime += (event.getEndTime() - event.getStartTime());
        }
        return totTime;
    }

    private long overlappingTime(Collection<EventStartAndEnd> events0, Collection<EventStartAndEnd> events1) {
        long overlappingTime = 0;

        for (EventStartAndEnd e0 : events0) {
            for (EventStartAndEnd e1 : events1) {
                overlappingTime += EventStartAndEnd.overlappingTime(e0.getStartTime(), e0.getEndTime(), e1.getStartTime(), e1.getEndTime());
            }
        }
        return overlappingTime;
    }

    private int overlappingDays(Collection<EventStartAndEnd> events0, Collection<EventSingleDay> events1) {
        int days = 0;

        for (EventSingleDay e1 : events1) {
            for (EventStartAndEnd e0 : events0) {
                Date sleepingDate = EventAgitation.getSleepingDate(e0.getStartTime(), e0.getEndTime());
                //System.out.println("StartTime: " +e0.getStartTime()+" EndTime: "+e0.getEndTime()+" Sleeping date: "+sleepingDate);
                if (EventAgitation.sameDay(sleepingDate, e1.getDate())) {
                    days++;
                    break;
                }

            }
        }
        return days;
    }

    private Collection<EventAgitation> overlappingEventsAgitationScoobyDoo(Collection<EventAgitation> events0, Collection<EventScoobyDoo> events1) {
        Collection<EventAgitation> eventAgitationScoobyDoo = new ArrayList<EventAgitation>();

        for (EventScoobyDoo e1 : events1) {
            for (EventAgitation e0 : events0) {
                Date sleepingDate = EventAgitation.getSleepingDate(e0.getStartTime(), e0.getEndTime());

                if (EventAgitation.sameDay(sleepingDate, e1.getDate())) {
                    eventAgitationScoobyDoo.add(e0);
                }

            }
        }
        return eventAgitationScoobyDoo;
    }

    private Collection<EventStartAndEnd> findStartAndEndEvents(ArrayList<ElaboratedSample> samples) {
        long startTime = samples.get(0).getTimestamp();
        long prevTime = startTime;

        Collection<EventStartAndEnd> sleepingEvents = new ArrayList<EventStartAndEnd>();
        Date day = new Date(prevTime);

        for (ElaboratedSample s : samples) {
            //System.out.println("Date: "+day+" Tot Time: "+totTime);

            if (!EventAgitation.sameDay(new Date(s.getTimestamp()), day)) {
                sleepingEvents.add(new EventSleeping(startTime, prevTime));
                startTime = s.getTimestamp();
                prevTime = startTime;
                day = new Date(startTime);
            }
            prevTime = s.getTimestamp();
        }
        sleepingEvents.add(new EventSleeping(startTime, prevTime));

        return sleepingEvents;
    }
}
